import { $set } from '@feng3d/serialization';
import { BufferGeometry3D, DodecahedronGeometry3D, MeshLambertMaterial3D, Node3D, PerspectiveCamera3D, PointsMaterial3D, Scene3D, TextureLoader3D, Vector3, WebGLRenderer3D } from 'feng3d-three';
import '../../extension/controls/OrbitControls3D';
import { ConvexGeometry3D } from '../../extension/geometries/ConvexGeometry3D';
import { BufferGeometryUtils3D } from '../../extension/utils/BufferGeometryUtils3D';

const rootPath = '../../';

let group: Node3D;
let camera: PerspectiveCamera3D;
let scene: Scene3D;
let renderer: WebGLRenderer3D;

init();
animate();

function init()
{
    scene = new Node3D().addComponent('Scene3D');

    renderer = scene.addComponent('WebGLRenderer3D', { parameters: { antialias: true } });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // camera

    camera = new Node3D().addComponent('PerspectiveCamera3D', { fov: 40, aspect: window.innerWidth / window.innerHeight, near: 1, far: 1000 });
    camera.entity.position.set(15, 20, 30);
    scene.entity.addChild(camera.entity);

    // controls

    const controls = scene.addComponent('OrbitControls3D', { camera, domElement: renderer.domElement });
    controls.minDistance = 20;
    controls.maxDistance = 50;
    controls.maxPolarAngle = Math.PI / 2;

    // ambient light

    scene.addComponent('AmbientLight3D', { color: { value: 0x222222 } });

    // point light

    const light = new Node3D().addComponent('PointLight3D', { color: { value: 0xffffff }, intensity: 1 });
    camera.entity.addChild(light.entity);

    // helper

    scene.entity.addChild(new Node3D().addComponent('AxesHelper3D', { size: 20 }).entity);

    // textures

    const loader = new TextureLoader3D();
    const texture = loader.load(`${rootPath}textures/sprites/disc.png`);

    group = new Node3D();
    scene.entity.addChild(group);

    // points

    let dodecahedronGeometry = $set(new DodecahedronGeometry3D(), { radius: 10 });

    // if normal and uv attributes are not removed, mergeVertices() can't consolidate indentical vertices with different normal/uv data

    dodecahedronGeometry.deleteAttribute('normal');
    dodecahedronGeometry.deleteAttribute('uv');

    dodecahedronGeometry = BufferGeometryUtils3D.mergeVertices(dodecahedronGeometry);

    const vertices: Vector3[] = [];
    const positionAttribute = dodecahedronGeometry.getAttribute('position');

    for (let i = 0; i < positionAttribute.count; i++)
    {
        const vertex = new Vector3();
        vertex.fromBufferAttribute(positionAttribute._bufferAttribute as any, i);
        vertices.push(vertex);
    }

    const pointsMaterial = $set(new PointsMaterial3D(), {
        color: { value: 0x0080ff },
        map: texture,
        size: 1,
        alphaTest: 0.5
    });

    const pointsGeometry = new BufferGeometry3D().setFromPoints(vertices);

    const points = new Node3D().addComponent('Points3D', { geometry: pointsGeometry, material: pointsMaterial });
    group.addChild(points.entity);

    // convex hull

    const meshMaterial = $set(new MeshLambertMaterial3D(), {
        color: { value: 0xffffff },
        opacity: 0.5,
        side: 'DoubleSide',
        transparent: true
    });

    const meshGeometry = $set(new ConvexGeometry3D(), { points: vertices });

    const mesh = new Node3D().addComponent('Mesh3D', { geometry: meshGeometry, material: meshMaterial });
    group.addChild(mesh.entity);

    //

    window.addEventListener('resize', onWindowResize);
}

function onWindowResize()
{
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate()
{
    requestAnimationFrame(animate);

    group.rotation.y += 0.005;

    render();
}

function render()
{
    renderer.render(scene, camera);
}
